/*

CloudLink Ω API extension for Scratch 3

Copyright (C) 2025 Mike Renaker "MikeDEV".

MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

(function (Scratch) {
    // Define class for authentication
    class OmegaAuth {
        constructor() {
            this.rootApiURL = "https://omega.mikedev101.cc/api/v0";
            this.rootWsURL = "wss://omega.mikedev101.cc/signaling";
            this.rootAuthURL = "https://omega.mikedev101.cc/accounts/api/v0"
            this.selectedUgi = "01HNPHRWS0N0AYMM5K4HN31V4W"; // Default UGI
            this.registerSuccess = false;
            this.loginSuccess = false;
            this.saveSuccess = false;
            this.loadSuccess = false;
            this.verifySuccess = false;
            this.resendSuccess = false;
            this.sessionToken = new String();
            this.statusCodes = {
                register: "",
                login: "",
                load: "",
                save: "",
                verify: "",
                resend: ""
            }
            this.loadedData = "";
        }

        async Login(email, password, totp) {
            try {
                const response = await fetch(`${this.rootAuthURL}/login`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        email,
                        password,
                        totp
                    }),
                });

                const data = await response.text(); // text/plain response. Should be just "OK".
                if (response.ok) {
                    console.log("账户登录成功。");
                    this.sessionToken = data;

                } else {
                    console.warn("账户登录失败：", data);
                }
                this.loginSuccess = response.ok;
                this.statusCodes.login = response.status;
            } catch (error) {
                console.error('Error getting login token:', error);
            }
        }

        async GuestLogin(username) {
            if (username == "") {
                console.warn("访客登录失败：未提供用户名。");
                this.loginSuccess = false;
                this.statusCodes.login = "400";
                return;
            }

            try {
                const response = await fetch(`${this.rootAuthURL}/guest-login`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        username,
                    }),
                });

                const data = await response.text(); // text/plain response. Should be just "OK".
                if (response.ok) {
                    console.log("访客账户登录成功。");
                    this.sessionToken = data;

                } else {
                    console.warn("访客登录失败：", data);
                }
                this.loginSuccess = response.ok;
                this.statusCodes.login = response.status;
            } catch (error) {
                console.error('Error getting guest login token:', error);
            }
        }

        async Save(save_slot, save_data) {
            try {
                const response = await fetch(`${this.rootApiURL}/save`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        token: this.sessionToken,
                        ugi: this.selectedUgi,
                        save_slot,
                        save_data,
                    }),
                });

                const data = await response.text(); // text/plain response. Should be just "OK".
                if (response.ok) {
                    console.log("数据保存成功。");
                } else {
                    console.warn("保存失败：", data);
                }
                this.saveSuccess = response.ok;
                this.statusCodes.save = response.status;
            } catch (error) {
                console.error('Error saving data:', error);
            }
        }

        async Load(save_slot) {
            try {
                const response = await fetch(`${this.rootApiURL}/load`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        token: this.sessionToken,
                        ugi: this.selectedUgi,
                        save_slot,
                    }),
                });

                const data = await response.text(); // text/plain response. Should be just "OK".
                if (response.ok) {
                    console.log("数据加载成功。");
                    this.loadedData = data;
                } else {
                    console.warn("加载失败：", data);
                }
                this.loadSuccess = response.ok;
                this.statusCodes.load = response.status;
            } catch (error) {
                console.error('Error loading data:', error);
            }
        }

        async Register(email, username, password) {
            try {
                const response = await fetch(`${this.rootAuthURL}/register`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        email,
                        username,
                        password,
                    }),
                });

                const data = await response.text(); // text/plain response. Should be just "OK".
                if (data == 'OK' || data == "OK; Email verification disabled") {
                    console.log("账户注册成功。");
                    this.registerSuccess = true;
                } else {
                    console.warn("账户注册失败：", data);
                    this.registerSuccess = false;
                }
                this.statusCodes.register = response.status;
            } catch (error) {
                console.error('Error getting response:', error);
                this.registerSuccess = false;
            }
        }

        async Verify(code) {
            try {
                const response = await fetch(`${this.rootAuthURL}/verify`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        token: this.sessionToken,
                        code,
                    }),
                });

                const data = await response.text(); // text/plain response. Should be just "OK".
                if (data == 'OK') {
                    console.log("邮箱验证成功。");
                    
                } else {
                    console.warn("邮箱验证失败：", data);
                }
                this.verifySuccess = (data == 'OK');
                this.statusCodes.verify = response.status;
            } catch (error) {
                console.error('Error getting response:', error);
                this.verifySuccess = false;
            }
        }

        async ResendVerify() {
            try {
                const response = await fetch(`${this.rootAuthURL}/resend-verify`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        token: this.sessionToken,
                    }),
                });
                const data = await response.text(); // text/plain response. Should be just "OK".
                if (data == 'OK') {
                    console.log("已成功重新发送邮箱验证。");
                    
                } else {
                    console.warn("重新发送邮箱验证失败：", data);
                }
                this.resendSuccess = (data == 'OK');
                this.statusCodes.resend = response.status;
            } catch (error) {
                console.error('Error getting response:', error);
                this.resendSuccess = false;
            }
        }
    }

    // Initialize class for the extension
    const OmegaAuthInstance = new OmegaAuth();

    // Define the extension for the CLΩ service
    class CloudLinkOmega {
        constructor(Scratch) {
            this.vm = Scratch.vm; // VM
            this.runtime = Scratch.vm.runtime; // Runtime

            // Define icons
            this.blockIconURI = "";

            // Define menu icon
            this.menuIconURI = "";
        }

        // Define blocks used in the extension
        getInfo() {
            return {
                id: 'clomega',
                name: 'CLΩ',
                docsURI: 'https://github.com/cloudlink-omega/extension/wiki/Extension',
                blockIconURI: this.blockIconURI,
                menuIconURI: this.menuIconURI,
                color1: "#FF4D4C",
                color2: "#FF7473",
                color3: "#A13332",
                blocks: [
                    {
                        opcode: 'set_ugi',
                        blockType: Scratch.BlockType.COMMAND,
                        text: Scratch.translate('将 [UGI] 设置为唯一游戏 ID (ugi)'),
                        arguments: {
                            UGI: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: '01HNPHRWS0N0AYMM5K4HN31V4W',
                            },
                        }
                    },
                    {
                        opcode: 'change_api_url',
                        blockType: Scratch.BlockType.COMMAND,
                        text: Scratch.translate('对 API 调用使用 [URL]'),
                        arguments: {
                            URL: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: 'https://omega.mikedev101.cc/api/v1',
                            },
                        }
                    },
                    {
                        opcode: 'change_wss_url',
                        blockType: Scratch.BlockType.COMMAND,
                        text: Scratch.translate('对游戏服务器使用 [URL]'),
                        arguments: {
                            URL: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: 'wss://omega.mikedev101.cc/signaling',
                            },
                        }
                    },
                    {
                        opcode: 'change_auth_url',
                        blockType: Scratch.BlockType.COMMAND,
                        text: Scratch.translate('对身份验证使用 [URL]'),
                        arguments: {
                            URL: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: 'https://omega.mikedev101.cc/accounts/api/v0',
                            },
                        }
                    },
                    {
                        opcode: 'build_server_url',
                        blockType: Scratch.BlockType.REPORTER,
                        text: Scratch.translate('连接字符串'),
                    },
                    "---",
                    {
                        opcode: 'get_token',
                        blockType: Scratch.BlockType.REPORTER,
                        text: Scratch.translate('会话令牌'),
                    },
                    {
                        opcode: 'login_status_code',
                        blockType: Scratch.BlockType.REPORTER,
                        text: Scratch.translate('登录状态码'),
                    },
                    {
                        opcode: 'was_login_successful',
                        blockType: Scratch.BlockType.BOOLEAN,
                        text: Scratch.translate('登录是否成功？'),
                    },
                    {
                        opcode: 'guest_login',
                        blockType: Scratch.BlockType.COMMAND,
                        text: Scratch.translate('以访客身份登录，用户名： [USERNAME]'),
                        arguments: {
                            USERNAME: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: '',
                            },
                        }
                    },
                    {
                        opcode: 'login_account',
                        blockType: Scratch.BlockType.COMMAND,
                        text: Scratch.translate('使用邮箱登录： [EMAIL] 密码： [PASSWORD] TOTP： [TOTP]'),
                        arguments: {
                            EMAIL: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: '',
                            },
                            PASSWORD: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: '',
                            },
                            TOTP: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: '',
                            }
                        }
                    },
                    "---",
                    {
                        opcode: 'register_status_code',
                        blockType: Scratch.BlockType.REPORTER,
                        text: Scratch.translate('注册状态码'),
                    },
                    {
                        opcode: 'was_register_successful',
                        blockType: Scratch.BlockType.BOOLEAN,
                        text: Scratch.translate('注册是否成功？'),
                    },
                    {
                        opcode: 'register_account',
                        blockType: Scratch.BlockType.COMMAND,
                        text: Scratch.translate('使用邮箱注册： [EMAIL] 用户名： [USERNAME] 密码： [PASSWORD]'),
                        arguments: {
                            EMAIL: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: '',
                            },
                            USERNAME: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: '',
                            },
                            PASSWORD: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: '',
                            }
                        }
                    },
                    "---",
                    {
                        blockType: Scratch.BlockType.LABEL,
                        text: Scratch.translate(`请先登录，然后再验证您的邮箱。`),
                    },
                    {
                        opcode: 'verify_status_code',
                        blockType: Scratch.BlockType.REPORTER,
                        text: Scratch.translate('邮箱验证状态码'),
                    },
                    {
                        opcode: 'was_verify_successful',
                        blockType: Scratch.BlockType.BOOLEAN,
                        text: Scratch.translate('邮箱验证是否成功？'),
                    },
                    {
                        opcode: 'verify_account',
                        blockType: Scratch.BlockType.COMMAND,
                        text: Scratch.translate('使用验证码验证邮箱： [CODE]'),
                        arguments: {
                            CODE: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: '',
                            }
                        }
                    },
                    "---",
                    {
                        opcode: 'resend_status_code',
                        blockType: Scratch.BlockType.REPORTER,
                        text: Scratch.translate('重新发送邮箱验证状态码'),
                    },
                    {
                        opcode: 'was_resend_successful',
                        blockType: Scratch.BlockType.BOOLEAN,
                        text: Scratch.translate('重新发送邮箱验证码是否成功？'),
                    },
                    {
                        opcode: 'resend_verify',
                        blockType: Scratch.BlockType.COMMAND,
                        text: Scratch.translate('重新发送邮箱验证码'),
                    },
                    "---",
                    {
                        opcode: 'save_status_code',
                        blockType: Scratch.BlockType.REPORTER,
                        text: Scratch.translate('保存状态码'),
                    },
                    {
                        opcode: 'was_save_successful',
                        blockType: Scratch.BlockType.BOOLEAN,
                        text: Scratch.translate('保存是否成功？'),
                    },
                    {
                        opcode: 'save_slot',
                        blockType: Scratch.BlockType.COMMAND,
                        text: Scratch.translate('写入保存槽 [SLOT]： [DATA]'),
                        arguments: {
                            SLOT: {
                                type: Scratch.ArgumentType.NUMBER,
                                defaultValue: "1",
                                menu: "SlotMenu",
                            },
                            DATA: {
                                type: Scratch.ArgumentType.STRING,
                                defaultValue: '要保存的内容',
                            },
                        }
                    },
                    "---",
                    {
                        opcode: 'load_status_code',
                        blockType: Scratch.BlockType.REPORTER,
                        text: Scratch.translate('加载状态码'),
                    },
                    {
                        opcode: 'was_load_successful',
                        blockType: Scratch.BlockType.BOOLEAN,
                        text: Scratch.translate('加载是否成功？'),
                    },
                    {
                        opcode: 'loaded_slot_data',
                        blockType: Scratch.BlockType.REPORTER,
                        text: Scratch.translate('已加载的保存数据'),
                    },
                    {
                        opcode: 'load_slot',
                        blockType: Scratch.BlockType.COMMAND,
                        text: Scratch.translate('从保存槽读取 [SLOT]'),
                        arguments: {
                            SLOT: {
                                type: Scratch.ArgumentType.NUMBER,
                                defaultValue: "1",
                                menu: "SlotMenu",
                            },
                        }
                    },
                ],
                menus: {
                    SlotMenu: {
                        acceptReporters: true,
                        items: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
                    }
                }
            };
        }

        change_api_url({ URL }) {
            OmegaAuthInstance.rootApiURL = Scratch.Cast.toString(URL);
        }

        change_wss_url({ URL }) {
            OmegaAuthInstance.rootWsURL = Scratch.Cast.toString(URL);
        }

        change_auth_url({ URL }) {
            OmegaAuthInstance.rootAuthURL = Scratch.Cast.toString(URL);
        }

        async login_account({ EMAIL, PASSWORD, TOTP }) {
            await OmegaAuthInstance.Login(Scratch.Cast.toString(EMAIL), Scratch.Cast.toString(PASSWORD), Scratch.Cast.toString(TOTP));
        }

        async guest_login({ USERNAME }) {
            await OmegaAuthInstance.GuestLogin(Scratch.Cast.toString(USERNAME));
        }

        register_status_code() {
            return OmegaAuthInstance.statusCodes.register;
        }

        login_status_code() {
            return OmegaAuthInstance.statusCodes.login;
        }

        save_status_code() {
            return OmegaAuthInstance.statusCodes.save;
        }

        load_status_code() {
            return OmegaAuthInstance.statusCodes.load;
        }

        was_save_successful() {
            return OmegaAuthInstance.saveSuccess;
        }

        async save_slot({ SLOT, DATA }) {
            await OmegaAuthInstance.Save(Scratch.Cast.toNumber(SLOT), Scratch.Cast.toString(DATA));
        }

        was_load_successful() {
            return OmegaAuthInstance.loadSuccess;
        }

        loaded_slot_data() {
            return OmegaAuthInstance.loadedData;
        }

        async load_slot({ SLOT }) {
            await OmegaAuthInstance.Load(SLOT);
        }

        was_login_successful() {
            return OmegaAuthInstance.loginSuccess;
        }

        async register_account({ EMAIL, USERNAME, PASSWORD }) {
            await OmegaAuthInstance.Register(Scratch.Cast.toString(EMAIL), Scratch.Cast.toString(USERNAME), Scratch.Cast.toString(PASSWORD));
            return OmegaAuthInstance.registerSuccess;
        }

        was_register_successful() {
            return OmegaAuthInstance.registerSuccess;
        }

        get_token() {
            return OmegaAuthInstance.sessionToken;
        }

        async verify_account({ CODE }) {
           await OmegaAuthInstance.Verify(Scratch.Cast.toString(CODE));
        }

        resend_status_code() {
            return OmegaAuthInstance.statusCodes.resend;
        }

        was_resend_successful() {
            return OmegaAuthInstance.resendSuccess;
        }

        async resend_verify() {
            await OmegaAuthInstance.ResendVerify();
        }

        verify_status_code() {
            return OmegaAuthInstance.statusCodes.verify;
        }

        was_verify_successful() {
            return OmegaAuthInstance.verifySuccess;
        }

        build_server_url() {
            let url = new URL(OmegaAuthInstance.rootWsURL);
            url.searchParams.append('ugi', OmegaAuthInstance.selectedUgi);
            return url.toString();
        }

        set_ugi({UGI}) {
            OmegaAuthInstance.selectedUgi = Scratch.Cast.toString(UGI);
        }
    }

    Scratch.extensions.register(new CloudLinkOmega(Scratch));
})(Scratch);
